package cn.nju.seg.atg.util;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

import cn.nju.seg.atg.GA.CrossoverModel;
import cn.nju.seg.atg.GA.StringGA;
import cn.nju.seg.atg.callCPP.CallCPP;
import cn.nju.seg.atg.parse.Builder;

/**
 * 测试数据自动生成主程序
 * 需要设置的参数：
 * 1.目标函数参数的潜在解域的边界：LEFT_BOUNDARY，RIGHT_BOUNDARY
 * 2.染色体中字符串表示的变量的长度：PARAM_LENGTH
 * 3. 随机值边界：PARAM_BOUNDARY
 * 4. 用户定制的输入参数的值：CUSTOMIZED_PARAMS
 * 
 * @author ChengXin
 *
 */
public class ATG extends StringGA{
	/**
	 * 目标函数的参数个数
	 */
	public static int NUM_OF_PARAM;
	
	/**
	 * 初始化为0的个体个数
	 */
	public static int CUSTOMIZED_ZERO_NUM = 0;
	
	/**
	 * 计算适应度函数值时设置的定值K
	 */
	public static double K = 10.0;
	
	/** 
	 * 目标函数参数的潜在解域的边界
	 */
	public static double LEFT_BOUNDARY = -64.0;
	public static double RIGHT_BOUNDARY = 64.0;
	 
	/**
     * 染色体中字符串表示的变量的长度
     */
	public static int PARAM_LENGTH = 8;
	
	/**
	 * 生成参数随机值的区间范围
	 * 第i(0,1,...)个参数的左右边界分别是[2*i]和[2*i+1]对应的值
	 */
	private static double[] PARAM_BOUNDARY = {-10, 10, -10, 10, -10, 10, -10, 10, -10, 10};
	
	/**
	 * 用户定制的输入参数值
	 */
	public static double[] CUSTOMIZED_PARAMS = {};
	
	/**
	 * 记录目标程序实际执行路径的数据文件
	 * CheckX
	 * Triangle
	 * IsValidDate
	 * IsLeapYear
	 * getRootOfQuadraticF
	 * Sum
	 * bessi
	 * bessj
	 * betacf
	 * caldat
	 * plgndr
	 * expint
	 * ei
	 * gammp
	 * julday
	 * ran1
	 * 
	 * my_abs
	 * my_fabs
	 * decode
	 * encode
	 * quantl
	 * logscl
	 * uppol2
	 * uppol1
	 * logsch
	 * 
	 * icrc
	 */
	
	/**
	 * 类c
	 * 为callFunction中的函数调用做准备
	 */
	@SuppressWarnings("rawtypes")
	private static Class c = null;
	/**
	 * 为callFunction中的函数调用做准备
	 */
	private static CallCPP callCPP = null;
	/**
	 * 参数类型
	 * 为callFunction中函数调用做准备
	 */
	@SuppressWarnings("rawtypes")
	private static Class[] parameterTypes;
	/**
	 * 参数
	 * 为callFunction中函数调用做准备
	 */
	private static Object[] parameters;
	/**
	 * CallCPP中需要执行的函数名
	 */
	private static String callFunctionName;
	
    private static String pathFile;

	public static int is_covered = -1;
	
	public ATG(){
		super(NUM_OF_PARAM * PARAM_LENGTH,//染色体长度
			  50,//个体总数
			  0.7,//交叉概率
			  10,//随机选择概率
			  200,//最大迭代次数
			  0,//初始种群中经预筛选的个体数目
			  2,//筛选个体时的最大迭代次数
			  0.1,//变异概率
			  0,//个体串中小数点位置
			  CrossoverModel.ctRoulette,//交叉方式
			  true);//数据统计
	}

	@Override
	protected double getFitness(int iChromIndex){
		for (int i=0; i<NUM_OF_PARAM; i++){
			Builder.parameters[i] =(LEFT_BOUNDARY + ((RIGHT_BOUNDARY - LEFT_BOUNDARY)/(Math.pow(2.0,PARAM_LENGTH)))*StringGA.getChromValAsDoubleArray(this.getChromosome(iChromIndex).getGenesAsStr())[i]);
		}
		//Builder.parameters[1] =(-1000 + ((3000 - (-1000))/(Math.pow(2.0,PARAM_LENGTH)))*StringGA.getChromValAsDoubleArray(this.getChromosome(iChromIndex).getGenesAsStr())[1]);
		//Builder.parameters[2] =(-1000 + ((4000 )/(Math.pow(2.0,PARAM_LENGTH)))*StringGA.getChromValAsDoubleArray(this.getChromosome(iChromIndex).getGenesAsStr())[2]);
		
		return generateTestDataForChrom();
	}
	/**
	 * ATG核心过程
	 * @param pathIndex
	 * @return
	 * @throws GAException 
	 */
	public static int generateTestData()
	{
		try {
			c = Class.forName("cn.nju.seg.atg.callCPP.CallCPP");
			callCPP = (CallCPP)c.newInstance();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		callFunctionName = "call" + (Builder.funcName.charAt(0)+"").toUpperCase() + Builder.funcName.substring(1, Builder.funcName.indexOf("("));
		pathFile = "/home/meizi/atg_data/" + Builder.funcName.substring(0, Builder.funcName.indexOf("(")) + ".dat";		
		
		Builder.parameters = new double[NUM_OF_PARAM];
		
		
		generateTestDataForChrom();
		
//		ATG GAforATG = new ATG();
//		GAforATG.run();

		return ATG.is_covered;
    }
	private static double generateTestDataForChrom(){
		double fitness = 0;
	    //初始化覆盖到的最长路径片段为空
		CFGPath maxPath = new CFGPath();
		//初始化变量产生池
		initialParameter(null);
		//打印当前参数
		printParams();
		//执行程序
		callFunction();
		//读取路径
		List<CFGPath> excutedPaths1 = PathUtil.readPath(pathFile);
		//计算当前时刻目标路径被覆盖到的部分路径中最长的一条
		CFGPath coveredPath1 = getCoveredPath(excutedPaths1, Builder.targetPath);		
		//更新目标路径能被覆盖到的最长部分路径
		maxPath.update(coveredPath1);
		
		//计算已覆盖部分的适应度值
/*		Iterator<SimpleCFGNode> itr = maxPath.getPath().iterator();
		while(itr.hasNext()){
			SimpleCFGNode nextNode = itr.next();
			System.out.println("CFGNode:"+nextNode.getName().toString());
			if(nextNode.isBranchNode()){
				double funValue = nextNode.getValue();
				System.out.println("value:"+nextNode.getValue());
			    System.out.println("isTrue:"+nextNode.isTrue());
			    if(!nextNode.isTrue()){
			    	funValue = -funValue;
			    }
			    if(funValue < 0){
			    	funValue = 0;
			    }
			    
			    fitness +=100/(1+funValue);	
		    
				System.out.println("isTrue:"+nextNode.isTrue());
				HashMap<String, Double> funValue = nextNode.getValues();
				Iterator<Map.Entry<String, Double>> iter = funValue.entrySet().iterator();
				while (iter.hasNext()) {
			    @SuppressWarnings("rawtypes")
				Map.Entry entry = (Map.Entry) iter.next();
			    System.out.println("key="+entry.getKey()+",value="+entry.getValue());
				}
			}
		}
		System.out.println("fitness:"+fitness);
       */
		//判断是否已覆盖目标路径
		boolean isCoveredTargetPath = maxPath.isTargetPath(Builder.targetPath);
     
		//返回结果
		if (isCoveredTargetPath)
		{
//			Builder.allPaths.get(Builder.pathIndex-1).setCovered(true);
//            Builder.allPaths.get(Builder.pathIndex-1).setOptimalParams(maxPath.getOptimalParams());
//			Builder.allPaths.get(Builder.pathIndex-1).setNumOfCoveredNodes(maxPath.getPath().size());
//			Builder.allPaths.get(Builder.pathIndex-1).setEndCoveredNodeName(maxPath.getEndNodeName());
			
//			ATG.is_covered = Builder.pathIndex;
		}
		else
		{ 
			//移除其他未执行过ATG的路径中被当前输入向量覆盖的那条
//			removeUncheckPath(excutedPaths1);
			//移除已执行过ATG后仍未被覆盖的路径集合中被当前输入向量覆盖的那条
//			removeUncoveredPath(excutedPaths1);
			
//			Builder.allPaths.get(Builder.pathIndex-1).setCovered(false);
//			if (Builder.allPaths.get(Builder.pathIndex-1).getNumOfCoveredNodes() < maxPath.getPath().size())
//			{
//				Builder.allPaths.get(Builder.pathIndex-1).setOptimalParams(maxPath.getOptimalParams());
//				Builder.allPaths.get(Builder.pathIndex-1).setNumOfCoveredNodes(maxPath.getPath().size());
//				Builder.allPaths.get(Builder.pathIndex-1).setEndCoveredNodeName(maxPath.getEndNodeName());
//			}

            ATG.is_covered = -1;
		}
		
		return fitness;
	}
	
	/**
	 * 执行待测试函数
	 */
	@SuppressWarnings({ "unchecked" })
	private static void callFunction()
	{
		//开始计时
		Builder.function_time = Builder.function_time - System.currentTimeMillis();
		
//		Builder.callCPP.callTriangle((int)Builder.parameters[0], (int)Builder.parameters[1], (int)Builder.parameters[2]);
		
		try {
			parameterTypes = new Class[NUM_OF_PARAM];
			setParameterTypes();
			parameters = new Object[NUM_OF_PARAM];
			setParameters();
			c.getMethod(callFunctionName, parameterTypes).invoke(callCPP, parameters);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//结束计时
		Builder.function_time = Builder.function_time + System.currentTimeMillis();
		//记录目标程序被执行一次
		Builder.function_frequency++;
	}
	
	/**
	 * 设置参数类型
	 * @param parameterTypes
	 */
	private static void setParameterTypes()
	{
		for (int i=0; i<NUM_OF_PARAM; i++)
		{
			if (Builder.parameterTypes[i].equals("int"))
			{
				parameterTypes[i] = int.class;
			}
			else if (Builder.parameterTypes[i].equals("double"))
			{
				parameterTypes[i] = double.class;
			}
			else if (Builder.parameterTypes[i].equals("float"))
			{
				parameterTypes[i] = float.class;
			}
			else if (Builder.parameterTypes[i].equals("short"))
			{
				parameterTypes[i] = short.class;
			}
			else if (Builder.parameterTypes[i].equals("unsigned short"))
			{
				parameterTypes[i] = int.class;
			}
			else if (Builder.parameterTypes[i].equals("unsigned long"))
			{
				parameterTypes[i] = long.class;
			}
		}
	}
	
	/**
	 * 设置参数
	 * @param parameters
	 */
	private static void setParameters()
	{
		for (int i=0; i<NUM_OF_PARAM; i++)
		{
			if (Builder.parameterTypes[i].equals("int"))
			{
				parameters[i] = (int)Builder.parameters[i];
			}
			else if (Builder.parameterTypes[i].equals("long"))
			{
				parameters[i] = (long)Builder.parameters[i];
			}
			else if (Builder.parameterTypes[i].equals("double"))
			{
				parameters[i] = (double)Builder.parameters[i];
			}
			else if (Builder.parameterTypes[i].equals("float"))
			{
				parameters[i] = (float)Builder.parameters[i];
			}
			else if (Builder.parameterTypes[i].equals("short"))
			{
				parameters[i] = (int)Builder.parameters[i];
			}
			else if (Builder.parameterTypes[i].equals("unsigned short"))
			{
				parameters[i] = (int)Builder.parameters[i];
			}
			else if (Builder.parameterTypes[i].equals("unsigned long"))
			{
				parameters[i] = (long)Builder.parameters[i];
			}
		}
	}
	
	/**
	 * 计算目标路径被覆盖到的最长部分路径
	 * @param excutedPaths
	 * @param targetPath
	 * @return
	 */
	private static CFGPath getCoveredPath(List<CFGPath> excutedPaths, CFGPath targetPath)
	{
		CFGPath coveredPath = new CFGPath();
		
		int size = excutedPaths.size();
		for (int i=0; i<size; i++)
		{
			CFGPath tempPath = excutedPaths.get(i).getCoveredPath(targetPath);
			if (tempPath.getPath().size() > coveredPath.getPath().size())
			{
				coveredPath = tempPath;
			}
		}
		
		return coveredPath;
	}
	
	/**
	 * 初始化参数值
	 */
	private static void initialParameter(double[] initParams)
	{
		if (initParams != null)
		{	// 手动设置初始值
			for (int i=0; i<NUM_OF_PARAM; i++)
			{
				Builder.parameters[i] = initParams[i];
			}
		}
		else
		{	// 根据取值区间随机生成初始值
			for (int i=0; i<NUM_OF_PARAM; i++)
			{
				Builder.parameters[i] = 2 * i +10;
			}
		}
	}
	
	/**
	 * 将第paramIndex个参数设为随机值
	 * @param paramIndex
	 * @param leftBoundary
	 * @param rightBoundary
	 * @param mode
	 * @INTERVAL_BOTH_OPEN：在(leftBoundary, rightBoundary)内产生随机值
	 * @INTERVAL_RIGHT_OPEN：在[leftBoundary, rightBoundary)内产生随机值
	 * @INTERVAL_LEFT_OPEN：在(leftBoundary, rightBoundary]内产生随机值
	 * @INTERVAL_BOTH_CLOSED：在[leftBoundary, rightBoundary]内产生随机值
	 */
	private static void setRandom(int paramIndex, int mode)
	{
		double leftBoundary = PARAM_BOUNDARY[paramIndex*2];
		double rightBoundary = PARAM_BOUNDARY[paramIndex*2+1];
		
		if (mode == ConstantValue.INTERVAL_BOTH_OPEN)
		{
			double random = Math.random();
			while(random == 0.0)
			{
				random = Math.random();
			}
			Builder.parameters[paramIndex] = random*(rightBoundary - leftBoundary) + leftBoundary;
		}
		else if (mode == ConstantValue.INTERVAL_LEFT_OPEN)
		{
			Builder.parameters[paramIndex] = rightBoundary - Math.random()*(rightBoundary - leftBoundary);
		}
		else if (mode == ConstantValue.INTERVAL_RIGHT_OPEN)
		{
			Builder.parameters[paramIndex] = Math.random()*(rightBoundary - leftBoundary) + leftBoundary;
		}
		else if (mode == ConstantValue.INTERVAL_BOTH_CLOSED)
		{
			if (Math.random() < 0.5)
			{
				Builder.parameters[paramIndex] = Math.random()*(rightBoundary - leftBoundary) + leftBoundary;
			}
			else
			{
				Builder.parameters[paramIndex] = rightBoundary - Math.random()*(rightBoundary - leftBoundary);
			}
		}
	}
	
	/**
	 * 打印当前参数(输入向量)
	 */
	private static void printParams()
	{
		String paramsStr = "";
		int paramsNum = Builder.parameters.length;
		for (int i=0; i<paramsNum; i++)
		{
			paramsStr += Builder.parameters[i]+", ";
		}
		System.out.println(paramsStr);
	}
	
	/**
	 * 移除尚未执行ATG的路径集合中已被覆盖到的一条路径
	 * 如果当前输入向量覆盖到uncheckedPaths中的某一条
	 * 则将该条路径移除，并标记为覆盖
	 * @param path
	 * @return
	 */
	private static boolean removeUncheckPath(List<CFGPath> paths)
	{
		int pathsSize = paths.size();
		int uncheckedPathsSize = Builder.uncheckedPaths.size();
		int i;
		for (i=0; i<uncheckedPathsSize; i++)
		{
			int j;
			for (j=0; j<pathsSize; j++)
			{
				if (paths.get(j).isEqual(Builder.uncheckedPaths.get(i).getPath()))
				{
					break;
				}
			}
			
			if (j < pathsSize)
			{
				break;
			}
		}
		
		if (i < uncheckedPathsSize)
		{
			Builder.countOfCoveredPath++;
			int pathIndex = Builder.getPathNum(Builder.uncheckedPaths.get(i));
			Builder.strCoveredPath += pathIndex + ",";
			
			Builder.allPaths.get(pathIndex-1).setCovered(true);
			Builder.allPaths.get(pathIndex-1).setOptimalParams(Builder.parameters);
			Builder.allPaths.get(pathIndex-1).setEndCoveredNodeName(Builder.allPaths.get(pathIndex-1).getEndNodeName());
			Builder.allPaths.get(pathIndex-1).setNumOfCoveredNodes(Builder.allPaths.get(pathIndex-1).getPath().size());
			
			// 添加已覆盖路径到coveredPaths
			Builder.coveredPaths.add(Builder.allPaths.get(pathIndex-1).clonePath());
			
			// 移除
			Builder.uncheckedPaths.remove(i);
			return true;
		}
		else
			return false;
	}
	
	/**
	 * 移除执行过ATG后仍未被覆盖到的路径集合中的一条路径
	 * 如果当前输入向量覆盖到uncoveredPaths中的某一条
	 * 则将该条路径移除，并标记为覆盖
	 * @param paths
	 * @return
	 */
	private static boolean removeUncoveredPath(List<CFGPath> paths)
	{
		int pathsSize = paths.size();
		int uncoveredPathsSize = Builder.uncoveredPaths.size();
		int i;
		for (i=0; i<uncoveredPathsSize; i++)
		{
			int j;
			for (j=0; j<pathsSize; j++)
			{
				if (paths.get(j).isEqual(Builder.uncoveredPaths.get(i).getPath()))
				{
					break;
				}
			}
			
			if (j < pathsSize)
			{
				break;
			}
		}
		
		if (i < uncoveredPathsSize)
		{
			Builder.countOfCoveredPath++;
			int pathIndex = Builder.getPathNum(Builder.uncoveredPaths.get(i));
			Builder.strCoveredPath += pathIndex + ",";
			
			Builder.allPaths.get(pathIndex-1).setCovered(true);
			Builder.allPaths.get(pathIndex-1).setOptimalParams(Builder.parameters);
			Builder.allPaths.get(pathIndex-1).setEndCoveredNodeName(Builder.allPaths.get(pathIndex-1).getEndNodeName());
			Builder.allPaths.get(pathIndex-1).setNumOfCoveredNodes(Builder.allPaths.get(pathIndex-1).getPath().size());
			
			// 添加已覆盖路径到coveredPaths
			Builder.coveredPaths.add(Builder.allPaths.get(pathIndex-1).clonePath());
			
			// 移除
			Builder.uncoveredPaths.remove(i);
			return true;
		}
		else
			return false;
	}
}
